<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <title>❤永远开心❤</title>

  <style>
    html, body {
      margin: 0px;
      width: 100%;
      height: 100%;
      overflow: hidden;
      background: #000;
    }

    #canvas {
      width: 100%;
      height: 100%;
    }

    html, body {
      height: 100%;
      padding: 0;
      margin: 0;
      background: #000;
    }

    canvas {
      position: absolute;
      width: 100%;
      height: 100%;
    }

    #child {
      position: absolute;
      left: 50%;
      top: 50%;
      transform: translate(-50%, -50%);

    }

    h4 {
      font-family: "STKaiti";
      font-size: 40px;
      color: #f584b7;
      position: relative;
    }
  </style>
</head>
<body>
<div id="child"><h4>💗老婆生日快乐💗</h4></div><!--这里写名字❤！！！-->
<canvas id="pinkboard"></canvas>

<script>
  /*
   * Settings
   */
  var settings = {
    particles: {
      length: 500, // maximum amount of particles
      duration: 2, // particle duration in sec
      velocity: 100, // particle velocity in pixels/sec
      effect: -0.75, // play with this for a nice effect
      size: 30, // particle size in pixels
    },
  };

  /*
   * RequestAnimationFrame polyfill by Erik M?ller
   */
  (function () {
    var b = 0;
    var c = ["ms", "moz", "webkit", "o"];
    for (var a = 0; a < c.length && !window.requestAnimationFrame; ++a) {
      window.requestAnimationFrame = window[c[a] + "RequestAnimationFrame"];
      window.cancelAnimationFrame = window[c[a] + "CancelAnimationFrame"] || window[c[a] + "CancelRequestAnimationFrame"]
    }
    if (!window.requestAnimationFrame) {
      window.requestAnimationFrame = function (h, e) {
        var d = new Date().getTime();
        var f = Math.max(0, 16 - (d - b));
        var g = window.setTimeout(function () {
          h(d + f)
        }, f);
        b = d + f;
        return g
      }
    }
    if (!window.cancelAnimationFrame) {
      window.cancelAnimationFrame = function (d) {
        clearTimeout(d)
      }
    }
  }());

  /*
   * Point class
   */
  var Point = (function () {
    function Point(x, y) {
      this.x = (typeof x !== 'undefined') ? x : 0;
      this.y = (typeof y !== 'undefined') ? y : 0;
    }

    Point.prototype.clone = function () {
      return new Point(this.x, this.y);
    };
    Point.prototype.length = function (length) {
      if (typeof length == 'undefined')
        return Math.sqrt(this.x * this.x + this.y * this.y);
      this.normalize();
      this.x *= length;
      this.y *= length;
      return this;
    };
    Point.prototype.normalize = function () {
      var length = this.length();
      this.x /= length;
      this.y /= length;
      return this;
    };
    return Point;
  })();

  /*
   * Particle class
   */
  var Particle = (function () {
    function Particle() {
      this.position = new Point();
      this.velocity = new Point();
      this.acceleration = new Point();
      this.age = 0;
    }

    Particle.prototype.initialize = function (x, y, dx, dy) {
      this.position.x = x;
      this.position.y = y;
      this.velocity.x = dx;
      this.velocity.y = dy;
      this.acceleration.x = dx * settings.particles.effect;
      this.acceleration.y = dy * settings.particles.effect;
      this.age = 0;
    };
    Particle.prototype.update = function (deltaTime) {
      this.position.x += this.velocity.x * deltaTime;
      this.position.y += this.velocity.y * deltaTime;
      this.velocity.x += this.acceleration.x * deltaTime;
      this.velocity.y += this.acceleration.y * deltaTime;
      this.age += deltaTime;
    };
    Particle.prototype.draw = function (context, image) {
      function ease(t) {
        return (--t) * t * t + 1;
      }

      var size = image.width * ease(this.age / settings.particles.duration);
      context.globalAlpha = 1 - this.age / settings.particles.duration;
      context.drawImage(image, this.position.x - size / 2, this.position.y - size / 2, size, size);
    };
    return Particle;
  })();

  /*
   * ParticlePool class
   */
  var ParticlePool = (function () {
    var particles,
            firstActive = 0,
            firstFree = 0,
            duration = settings.particles.duration;

    function ParticlePool(length) {
      // create and populate particle pool
      particles = new Array(length);
      for (var i = 0; i < particles.length; i++)
        particles[i] = new Particle();
    }

    ParticlePool.prototype.add = function (x, y, dx, dy) {
      particles[firstFree].initialize(x, y, dx, dy);

      // handle circular queue
      firstFree++;
      if (firstFree == particles.length) firstFree = 0;
      if (firstActive == firstFree) firstActive++;
      if (firstActive == particles.length) firstActive = 0;
    };
    ParticlePool.prototype.update = function (deltaTime) {
      var i;

      // update active particles
      if (firstActive < firstFree) {
        for (i = firstActive; i < firstFree; i++)
          particles[i].update(deltaTime);
      }
      if (firstFree < firstActive) {
        for (i = firstActive; i < particles.length; i++)
          particles[i].update(deltaTime);
        for (i = 0; i < firstFree; i++)
          particles[i].update(deltaTime);
      }

      // remove inactive particles
      while (particles[firstActive].age >= duration && firstActive != firstFree) {
        firstActive++;
        if (firstActive == particles.length) firstActive = 0;
      }


    };
    ParticlePool.prototype.draw = function (context, image) {
      // draw active particles
      if (firstActive < firstFree) {
        for (i = firstActive; i < firstFree; i++)
          particles[i].draw(context, image);
      }
      if (firstFree < firstActive) {
        for (i = firstActive; i < particles.length; i++)
          particles[i].draw(context, image);
        for (i = 0; i < firstFree; i++)
          particles[i].draw(context, image);
      }
    };
    return ParticlePool;
  })();

  /*
   * Putting it all together
   */
  (function (canvas) {
    var context = canvas.getContext('2d'),
            particles = new ParticlePool(settings.particles.length),
            particleRate = settings.particles.length / settings.particles.duration, // particles/sec
            time;

    // get point on heart with -PI <= t <= PI
    function pointOnHeart(t) {
      return new Point(
              160 * Math.pow(Math.sin(t), 3),
              130 * Math.cos(t) - 50 * Math.cos(2 * t) - 20 * Math.cos(3 * t) - 10 * Math.cos(4 * t) + 25
      );
    }

    // creating the particle image using a dummy canvas
    var image = (function () {
      var canvas = document.createElement('canvas'),
              context = canvas.getContext('2d');
      canvas.width = settings.particles.size;
      canvas.height = settings.particles.size;

      // helper function to create the path
      function to(t) {
        var point = pointOnHeart(t);
        point.x = settings.particles.size / 2 + point.x * settings.particles.size / 350;
        point.y = settings.particles.size / 2 - point.y * settings.particles.size / 350;
        return point;
      }

      // create the path
      context.beginPath();
      var t = -Math.PI;
      var point = to(t);
      context.moveTo(point.x, point.y);
      while (t < Math.PI) {
        t += 0.01; // baby steps!
        point = to(t);
        context.lineTo(point.x, point.y);
      }
      context.closePath();
      // create the fill
      context.fillStyle = '#ea80b0';
      context.fill();
      // create the image
      var image = new Image();
      image.src = canvas.toDataURL();
      return image;
    })();

    // render that thing!
    function render() {
      // next animation frame
      requestAnimationFrame(render);

      // update time
      var newTime = new Date().getTime() / 1000,
              deltaTime = newTime - (time || newTime);
      time = newTime;

      // clear canvas
      context.clearRect(0, 0, canvas.width, canvas.height);

      // create new particles
      var amount = particleRate * deltaTime;
      for (var i = 0; i < amount; i++) {
        var pos = pointOnHeart(Math.PI - 2 * Math.PI * Math.random());
        var dir = pos.clone().length(settings.particles.velocity);
        particles.add(canvas.width / 2 + pos.x, canvas.height / 2 - pos.y, dir.x, -dir.y);
      }

      // update and draw particles
      particles.update(deltaTime);
      particles.draw(context, image);
    }

    // handle (re-)sizing of the canvas
    function onResize() {
      canvas.width = canvas.clientWidth;
      canvas.height = canvas.clientHeight;
    }

    window.onresize = onResize;

    // delay rendering bootstrap
    setTimeout(function () {
      onResize();
      render();
    }, 10);
  })(document.getElementById('pinkboard'));</script>
<canvas id="canvas"></canvas>
<script>
  function initVars() {

    pi = Math.PI;
    ctx = canvas.getContext("2d");
    canvas.width = canvas.clientWidth;
    canvas.height = canvas.clientHeight;
    cx = canvas.width / 2;
    cy = canvas.height / 2;
    playerZ = -25;
    playerX = playerY = playerVX = playerVY = playerVZ = pitch = yaw = pitchV = yawV = 0;
    scale = 600;
    seedTimer = 0;
    seedInterval = 5, seedLife = 100;
    gravity = .02;
    seeds = new Array();
    sparkPics = new Array();
    s = "https://cantelope.org/NYE/";
    for (i = 1; i <= 10; ++i) {
      sparkPic = new Image();
      sparkPic.src = s + "spark" + i + ".png";
      sparkPics.push(sparkPic);
    }
    sparks = new Array();
    pow1 = new Audio(s + "pow1.ogg");
    pow2 = new Audio(s + "pow2.ogg");
    pow3 = new Audio(s + "pow3.ogg");
    pow4 = new Audio(s + "pow4.ogg");
    frames = 0;
  }

  function rasterizePoint(x, y, z) {

    var p, d;
    x -= playerX;
    y -= playerY;
    z -= playerZ;
    p = Math.atan2(x, z);
    d = Math.sqrt(x * x + z * z);
    x = Math.sin(p - yaw) * d;
    z = Math.cos(p - yaw) * d;
    p = Math.atan2(y, z);
    d = Math.sqrt(y * y + z * z);
    y = Math.sin(p - pitch) * d;
    z = Math.cos(p - pitch) * d;
    var rx1 = -1000, ry1 = 1, rx2 = 1000, ry2 = 1, rx3 = 0, ry3 = 0, rx4 = x, ry4 = z,
            uc = (ry4 - ry3) * (rx2 - rx1) - (rx4 - rx3) * (ry2 - ry1);
    if (!uc) return {x: 0, y: 0, d: -1};
    var ua = ((rx4 - rx3) * (ry1 - ry3) - (ry4 - ry3) * (rx1 - rx3)) / uc;
    var ub = ((rx2 - rx1) * (ry1 - ry3) - (ry2 - ry1) * (rx1 - rx3)) / uc;
    if (!z) z = .000000001;
    if (ua > 0 && ua < 1 && ub > 0 && ub < 1) {
      return {
        x: cx + (rx1 + ua * (rx2 - rx1)) * scale,
        y: cy + y / z * scale,
        d: Math.sqrt(x * x + y * y + z * z)
      };
    } else {
      return {
        x: cx + (rx1 + ua * (rx2 - rx1)) * scale,
        y: cy + y / z * scale,
        d: -1
      };
    }
  }

  function spawnSeed() {

    seed = new Object();
    seed.x = -50 + Math.random() * 100;
    seed.y = 25;
    seed.z = -50 + Math.random() * 100;
    seed.vx = .1 - Math.random() * .2;
    seed.vy = -1.5;//*(1+Math.random()/2);
    seed.vz = .1 - Math.random() * .2;
    seed.born = frames;
    seeds.push(seed);
  }

  function splode(x, y, z) {

    t = 5 + parseInt(Math.random() * 150);
    sparkV = 1 + Math.random() * 2.5;
    type = parseInt(Math.random() * 3);
    switch (type) {
      case 0:
        pic1 = parseInt(Math.random() * 10);
        break;
      case 1:
        pic1 = parseInt(Math.random() * 10);
        do {
          pic2 = parseInt(Math.random() * 10);
        } while (pic2 == pic1);
        break;
      case 2:
        pic1 = parseInt(Math.random() * 10);
        do {
          pic2 = parseInt(Math.random() * 10);
        } while (pic2 == pic1);
        do {
          pic3 = parseInt(Math.random() * 10);
        } while (pic3 == pic1 || pic3 == pic2);
        break;
    }
    for (m = 1; m < t; ++m) {
      spark = new Object();
      spark.x = x;
      spark.y = y;
      spark.z = z;
      p1 = pi * 2 * Math.random();
      p2 = pi * Math.random();
      v = sparkV * (1 + Math.random() / 6)
      spark.vx = Math.sin(p1) * Math.sin(p2) * v;
      spark.vz = Math.cos(p1) * Math.sin(p2) * v;
      spark.vy = Math.cos(p2) * v;
      switch (type) {
        case 0:
          spark.img = sparkPics[pic1];
          break;
        case 1:
          spark.img = sparkPics[parseInt(Math.random() * 2) ? pic1 : pic2];
          break;
        case 2:
          switch (parseInt(Math.random() * 3)) {
            case 0:
              spark.img = sparkPics[pic1];
              break;
            case 1:
              spark.img = sparkPics[pic2];
              break;
            case 2:
              spark.img = sparkPics[pic3];
              break;
          }
          break;
      }
      spark.radius = 25 + Math.random() * 50;
      spark.alpha = 1;
      spark.trail = new Array();
      sparks.push(spark);
    }
    switch (parseInt(Math.random() * 4)) {
      case 0:
        pow = new Audio(s + "pow1.ogg");
        break;
      case 1:
        pow = new Audio(s + "pow2.ogg");
        break;
      case 2:
        pow = new Audio(s + "pow3.ogg");
        break;
      case 3:
        pow = new Audio(s + "pow4.ogg");
        break;
    }
    d = Math.sqrt((x - playerX) * (x - playerX) + (y - playerY) * (y - playerY) + (z - playerZ) * (z - playerZ));
    pow.volume = 1.5 / (1 + d / 10);
    pow.play();
  }

  function doLogic() {

    if (seedTimer < frames) {
      seedTimer = frames + seedInterval * Math.random() * 10;
      spawnSeed();
    }
    for (i = 0; i < seeds.length; ++i) {
      seeds[i].vy += gravity;
      seeds[i].x += seeds[i].vx;
      seeds[i].y += seeds[i].vy;
      seeds[i].z += seeds[i].vz;
      if (frames - seeds[i].born > seedLife) {
        splode(seeds[i].x, seeds[i].y, seeds[i].z);
        seeds.splice(i, 1);
      }
    }
    for (i = 0; i < sparks.length; ++i) {
      if (sparks[i].alpha > 0 && sparks[i].radius > 5) {
        sparks[i].alpha -= .01;
        sparks[i].radius /= 1.02;
        sparks[i].vy += gravity;
        point = new Object();
        point.x = sparks[i].x;
        point.y = sparks[i].y;
        point.z = sparks[i].z;
        if (sparks[i].trail.length) {
          x = sparks[i].trail[sparks[i].trail.length - 1].x;
          y = sparks[i].trail[sparks[i].trail.length - 1].y;
          z = sparks[i].trail[sparks[i].trail.length - 1].z;
          d = ((point.x - x) * (point.x - x) + (point.y - y) * (point.y - y) + (point.z - z) * (point.z - z));
          if (d > 9) {
            sparks[i].trail.push(point);
          }
        } else {
          sparks[i].trail.push(point);
        }
        if (sparks[i].trail.length > 5) sparks[i].trail.splice(0, 1);
        sparks[i].x += sparks[i].vx;
        sparks[i].y += sparks[i].vy;
        sparks[i].z += sparks[i].vz;
        sparks[i].vx /= 1.075;
        sparks[i].vy /= 1.075;
        sparks[i].vz /= 1.075;
      } else {
        sparks.splice(i, 1);
      }
    }
    p = Math.atan2(playerX, playerZ);
    d = Math.sqrt(playerX * playerX + playerZ * playerZ);
    d += Math.sin(frames / 80) / 1.25;
    t = Math.sin(frames / 200) / 40;
    playerX = Math.sin(p + t) * d;
    playerZ = Math.cos(p + t) * d;
    yaw = pi + p + t;
  }

  function rgb(col) {

    var r = parseInt((.5 + Math.sin(col) * .5) * 16);
    var g = parseInt((.5 + Math.cos(col) * .5) * 16);
    var b = parseInt((.5 - Math.sin(col) * .5) * 16);
    return "#" + r.toString(16) + g.toString(16) + b.toString(16);
  }

  function draw() {

    ctx.clearRect(0, 0, cx * 2, cy * 2);

    ctx.fillStyle = "#ff8";
    for (i = -100; i < 100; i += 3) {
      for (j = -100; j < 100; j += 4) {
        x = i;
        z = j;
        y = 25;
        point = rasterizePoint(x, y, z);
        if (point.d != -1) {
          size = 250 / (1 + point.d);
          d = Math.sqrt(x * x + z * z);
          a = 0.75 - Math.pow(d / 100, 6) * 0.75;
          if (a > 0) {
            ctx.globalAlpha = a;
            ctx.fillRect(point.x - size / 2, point.y - size / 2, size, size);
          }
        }
      }
    }
    ctx.globalAlpha = 1;
    for (i = 0; i < seeds.length; ++i) {
      point = rasterizePoint(seeds[i].x, seeds[i].y, seeds[i].z);
      if (point.d != -1) {
        size = 200 / (1 + point.d);
        ctx.fillRect(point.x - size / 2, point.y - size / 2, size, size);
      }
    }
    point1 = new Object();
    for (i = 0; i < sparks.length; ++i) {
      point = rasterizePoint(sparks[i].x, sparks[i].y, sparks[i].z);
      if (point.d != -1) {
        size = sparks[i].radius * 200 / (1 + point.d);
        if (sparks[i].alpha < 0) sparks[i].alpha = 0;
        if (sparks[i].trail.length) {
          point1.x = point.x;
          point1.y = point.y;
          switch (sparks[i].img) {
            case sparkPics[0]:
              ctx.strokeStyle = "#f84";
              break;
            case sparkPics[1]:
              ctx.strokeStyle = "#84f";
              break;
            case sparkPics[2]:
              ctx.strokeStyle = "#8ff";
              break;
            case sparkPics[3]:
              ctx.strokeStyle = "#fff";
              break;
            case sparkPics[4]:
              ctx.strokeStyle = "#4f8";
              break;
            case sparkPics[5]:
              ctx.strokeStyle = "#f44";
              break;
            case sparkPics[6]:
              ctx.strokeStyle = "#f84";
              break;
            case sparkPics[7]:
              ctx.strokeStyle = "#84f";
              break;
            case sparkPics[8]:
              ctx.strokeStyle = "#fff";
              break;
            case sparkPics[9]:
              ctx.strokeStyle = "#44f";
              break;
          }
          for (j = sparks[i].trail.length - 1; j >= 0; --j) {
            point2 = rasterizePoint(sparks[i].trail[j].x, sparks[i].trail[j].y, sparks[i].trail[j].z);
            if (point2.d != -1) {
              ctx.globalAlpha = j / sparks[i].trail.length * sparks[i].alpha / 2;
              ctx.beginPath();
              ctx.moveTo(point1.x, point1.y);
              ctx.lineWidth = 1 + sparks[i].radius * 10 / (sparks[i].trail.length - j) / (1 + point2.d);
              ctx.lineTo(point2.x, point2.y);
              ctx.stroke();
              point1.x = point2.x;
              point1.y = point2.y;
            }
          }
        }
        ctx.globalAlpha = sparks[i].alpha;
        // ctx.drawImage(sparks[i].img, point.x - size / 2, point.y - size / 2, size, size);
      }
    }
  }

  function frame() {

    if (frames > 100000) {
      seedTimer = 0;
      frames = 0;
    }
    frames++;
    draw();
    doLogic();
    requestAnimationFrame(frame);
  }

  window.addEventListener("resize", () => {
    canvas.width = canvas.clientWidth;
    canvas.height = canvas.clientHeight;
    cx = canvas.width / 2;
    cy = canvas.height / 2;
  });

  initVars();
  frame();</script>

<audio autoplay="autoplay" loop="loop" preload="auto" id="music" controls>
  <!--1851244378 星辰大海-->
  <source src="http://music.163.com/song/media/outer/url?id=1827600686.mp3" type="audio/ogg">
  <source src="http://music.163.com/song/media/outer/url?id=1827600686.mp3" type="audio/mpeg">
</audio>
</body>
</html>